
`include "common_header.verilog"

//  *************************************************************************
//  File : stats_maprxcnts
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited.
//  Copyright (c) 2009 MoreThanIP.com, Germany
//  Designed by : Daniel Koehler
//  support@morethanip.com
//  *************************************************************************
//  Description: Map the RX statistics vector to a counter vector for
//               mapping them into corresponding memory locations.
//               Remap the host read addresses into corresponding memory addresses
//               when reading the counters
//               
//  Version    : $Id: stats_maprxcnts.v,v 1.8 2017/07/07 08:12:52 gc Exp $
//  *************************************************************************

module stats_maprxcnts (
   
   reset,
   clk,
   frm_align_err,
`ifdef STAT_PFCCNT_ENA
   rsv_stat_pfc,
`endif          
   rsv_stat,
   rsv_stat_val,
   stat,
   stat_octetsok,
   stat_octets,
   stat_val_o,
   vec_busy_int,
   mreg_sel,
   stats_sel,
   stats_sel_vld);

`ifdef STAT_PFCCNT_ENA

parameter PFC_PRIORITIES = 8;                // PFC Priorities, either 8 or 16
parameter CNTNUM        = 32+PFC_PRIORITIES; //  width of final counter vector (one bit per counter)
parameter CNTADDR       = 6;                 //  address bits

`else

parameter CNTNUM        = 32;           //  width of final counter vector (one bit per counter)
parameter CNTADDR       = 5;            //  address bits

`endif

input   reset;                  //  async active high
input   clk;                    //  line clock
input   frm_align_err;          //  Received Frame Aligment Error Indication from MAC 
`ifdef STAT_PFCCNT_ENA
input   [PFC_PRIORITIES-1:0] rsv_stat_pfc;     //  the class enable of latest received PFC frame
`endif
input   [31:0] rsv_stat;        //  receive Statistic Vector from MAC RX
input   rsv_stat_val;           //  statistic vector selected and data valid, start the sequence
output  [CNTNUM-1:0] stat;      //  Statistic Vector one bit per counter
output  [15:0] stat_octetsok;   //  for octetsOK (payload octets)
output  [15:0] stat_octets;     //  for total octets
output  stat_val_o;             //  delayed stat_val to align with stat output being valid
input   vec_busy_int;           //  processing is ongoing, asserts following stat_val_o (handshake)
input   [7:0] mreg_sel;         //  address in MAC address space
output  [CNTADDR-1:0] stats_sel;//  address of counter to read (remapped, combinatorial)
output  stats_sel_vld;          //  indicate if counter address is valid (one of the herein managed counters has been addressed)

reg     [CNTNUM-1:0] stat; 
wire    [15:0] stat_octetsok; 
wire    [15:0] stat_octets; 
wire    stat_val_o; 
reg     [CNTADDR-1:0] stats_sel; 
wire    stats_sel_vld; 

reg     stat_val_d; 
reg     stat_val_o_int; 
reg     [31:0] rsv_stat_d;      //  receive Statistic Vector 32=abort
reg     dropped_d;              //  indicate if frame has been dropped.
reg     [15:0] stat_octets_int; //  for total octets
reg     algn_err_l; 

`ifdef STAT_PFCCNT_ENA
reg     [PFC_PRIORITIES-1:0] rsv_stat_pfc_d;
`endif


assign stat_octets   = stat_octets_int; 
assign stat_octetsok = stat_octets_int; //  not corrected by anything. Software needs to subtract 18*FramesOK if needed.
assign stat_val_o    = stat_val_o_int;

//  Alignment error (invalid SFD) is happening asynchronously to any normal frame statistics.
//  need to latch the event until the next regular statistics arrives. This means we may
//  drop the indication, which is fine as missing SFD could also be noise on the wire
//  so there is no accurate relation with a frame necessarily
//  --------------------------------------------------------------------------
always @(posedge reset or posedge clk)
   begin : palgn
   if (reset == 1'b 1)
      begin
      algn_err_l <= 1'b 0;	
      end
   else
      begin
      if (frm_align_err == 1'b 1)
         begin
         algn_err_l <= 1'b 1;	
         end
      else if (stat_val_d == 1'b 1 )
         begin
         algn_err_l <= 1'b 0;	
         end
      end
   end

//  map the statistics vector to counters and store (keep stable) for counting        
//  --------------------------------------------------------------------------
always @(posedge reset or posedge clk)
   begin : pstat
   if (reset == 1'b 1)
      begin
      stat       <= {CNTNUM{1'b 0}};
        // stat_octetsok   <= 16'h 0000;
      stat_octets_int <= 16'h 0000;	
      stat_val_d <= 1'b 0;	
      rsv_stat_d <= {32{1'b 0}};	
      dropped_d  <= 1'b 0;	
      stat_val_o_int <= 1'b 0;	
      
      `ifdef STAT_PFCCNT_ENA
      rsv_stat_pfc_d <= {PFC_PRIORITIES{1'b0}};
      `endif
      
      end
   else
      begin
      stat_val_d <= rsv_stat_val;	
      rsv_stat_d[31:16] <= rsv_stat[31:16];    

      `ifdef STAT_PFCCNT_ENA
      rsv_stat_pfc_d    <= rsv_stat_pfc;
      `endif
      
      if( rsv_stat_val==1'b 1 ) // keep length in case of overlapping
         begin
         rsv_stat_d[15:0] <= rsv_stat[15:0];
         end        

        //  mark if frame was dropped when:
        //  RxOK=1, U/M/BCast=0, PauseRx=0, CtrlRX=0, Truncated=0
      if (rsv_stat[20] == 1'b 1 & rsv_stat[21] == 1'b 0 & rsv_stat[22] == 1'b 0 & 
          rsv_stat[23] == 1'b 0 & rsv_stat[24] == 1'b 0 & rsv_stat[25] == 1'b 0 & 
          rsv_stat[28] == 1'b 0)
         begin
         dropped_d <= 1'b 1;	
         end
      else
         begin
         dropped_d <= 1'b 0;	
         end

      if (stat_val_d==1'b 1 & stat_val_o_int == 1'b 0 )
         begin
         stat_val_o_int <= 1'b 1;	//  one delayed
         end
      else if( vec_busy_int==1'b 0 )    // our stat_val_o is seen by stats_vectoaddr when it is not busy with a previous vector
         begin
         stat_val_o_int <= 1'b 0;
         end

      //if (stat_val_d == 1'b 1 & stat_val_o_int == 1'b 0)        // ignore if previous has not been served yet
      if (vec_busy_int == 1'b 0)        // to output only if output is not processing previous
         begin
            // stat_octetsok <= rsv_stat_d[15:0] - 16'h 0012;      // total-18 => we only want the payload octets
            stat_octets_int <= rsv_stat_d[15:0];	
         end

        //  final output vector generation
        //  ------------------------------
      if (stat_val_d == 1'b 1 & stat_val_o_int == 1'b 0)        // ignore if previous has not been served yet
         begin
        //  count statistics only if not for an aborted frame
        //  0: etherStatsOctets  (counts for all)
         stat[0] <= 1'b 1;	
        //  1: OctetsOK
         if (rsv_stat_d[20] == 1'b 1 & dropped_d == 1'b 0)
            begin
            stat[1] <= 1'b 1;	
            end
         else
            begin
            stat[1] <= 1'b 0;	
            end

        //  2: aAlignmentErrors
         stat[2] <= algn_err_l;	

        //  3: PauseOK
         if (rsv_stat_d[25] == 1'b 1)
            begin
            stat[3] <= 1'b 1;	
            end
         else
            begin
            stat[3] <= 1'b 0;	
            end

        //  4: FramesOK
         if (rsv_stat_d[20] == 1'b 1 & dropped_d == 1'b 0)
            begin
            stat[4] <= 1'b 1;	
            end
         else
            begin
            stat[4] <= 1'b 0;	
            end

        //  5: CRCErr in good length frame
         if (rsv_stat_d[17] == 1'b 1 & rsv_stat_d[19] == 1'b 0 & rsv_stat_d[29] == 1'b 0)
            begin
            stat[5] <= 1'b 1;	
            end
         else
            begin
            stat[5] <= 1'b 0;	
            end

        //  6: VlanOK
         if (rsv_stat_d[20] == 1'b 1 & rsv_stat_d[26] == 1'b 1)
            begin
            stat[6] <= 1'b 1;	
            end
         else
            begin
            stat[6] <= 1'b 0;	
            end

        //  7: ifInErrors (except undersize/fragments according to RFC2665!)
         if (rsv_stat_d[20] == 1'b 0 & rsv_stat_d[29] == 1'b 0)
            begin
            stat[7] <= 1'b 1;	
            end
         else
            begin
            stat[7] <= 1'b 0;	
            end

        //  8: UCastOK
         if (rsv_stat_d[20] == 1'b 1 & rsv_stat_d[21] == 1'b 1)
            begin
            stat[8] <= 1'b 1;	
            end
         else
            begin
            stat[8] <= 1'b 0;	
            end

        //  9: MCastOK
         if (rsv_stat_d[20] == 1'b 1 & rsv_stat_d[22] == 1'b 1)
            begin
            stat[9] <= 1'b 1;	
            end
         else
            begin
            stat[9] <= 1'b 0;	
            end

        //  10 BCastOK
         if (rsv_stat_d[20] == 1'b 1 & rsv_stat_d[23] == 1'b 1)
            begin
            stat[10] <= 1'b 1;	
            end
         else
            begin
            stat[10] <= 1'b 0;	
            end

        //  11: Dropped/Truncated
         if (rsv_stat_d[28] == 1'b 1 | dropped_d == 1'b 1)
            begin
            stat[11] <= 1'b 1;	
            end
         else
            begin
            stat[11] <= 1'b 0;	
            end

        //  12: etherStatsPkts (all)
         stat[12] <= 1'b 1;	

        //  13: Undersize (good CRC)
         if (rsv_stat_d[17] == 1'b 0 & rsv_stat_d[29] == 1'b 1)
            begin
            stat[13] <= 1'b 1;	
            end
         else
            begin
            stat[13] <= 1'b 0;	
            end

        //  14: Pkt64
         if (rsv_stat_d[15:0] == 16'h 0040 & rsv_stat_d[19] == 1'b 0)
            begin
            stat[14] <= 1'b 1;	
            end
         else
            begin
            stat[14] <= 1'b 0;	
            end

        //  15: Pkt65
         if (rsv_stat_d[15:0] > 16'h 0040 & rsv_stat_d[15:7] == 9'b 000000000 & rsv_stat_d[19] == 1'b 0)
            begin
            stat[15] <= 1'b 1;	
            end
         else
            begin
            stat[15] <= 1'b 0;	
            end

        //  16: Pkt128
         if (rsv_stat_d[15:7] == 9'b 000000001 & rsv_stat_d[19] == 1'b 0)
            begin
            stat[16] <= 1'b 1;	
            end
         else
            begin
            stat[16] <= 1'b 0;	
            end

        //  17: Pkt256
         if (rsv_stat_d[15:8] == 8'b 00000001 & rsv_stat_d[19] == 1'b 0)
            begin
            stat[17] <= 1'b 1;	
            end
         else
            begin
            stat[17] <= 1'b 0;	
            end

        //  18 Pkt512
         if (rsv_stat_d[15:9] == 7'b 0000001 & rsv_stat_d[19] == 1'b 0)
            begin
            stat[18] <= 1'b 1;	
            end
         else
            begin
            stat[18] <= 1'b 0;	
            end

        //  19: Pkt1024..1518
         if (rsv_stat_d[15:10] == 6'b 000001 & rsv_stat_d[15:0] < 16'h 05ef & rsv_stat_d[19] == 1'b 0)
            begin
            stat[19] <= 1'b 1;	
            end
         else
            begin
            stat[19] <= 1'b 0;	
            end

        //  20: Pkt1519..x
         if (rsv_stat_d[15:0] > 16'h 05ee & rsv_stat_d[19] == 1'b 0)
            begin
            stat[20] <= 1'b 1;	
            end
         else
            begin
            stat[20] <= 1'b 0;	
            end

        //  21: Oversize (good CRC)
         if (rsv_stat_d[17] == 1'b 0 & rsv_stat_d[19] == 1'b 1)
            begin
            stat[21] <= 1'b 1;	
            end
         else
            begin
            stat[21] <= 1'b 0;	
            end

        //  22: Jabber (bad CRC)
         if (rsv_stat_d[17] == 1'b 1 & rsv_stat_d[19] == 1'b 1)
            begin
            stat[22] <= 1'b 1;	
            end
         else
            begin
            stat[22] <= 1'b 0;	
            end

        //  23: Fragments (bad CRC)
         if (rsv_stat_d[17] == 1'b 1 & rsv_stat_d[29] == 1'b 1)
            begin
            stat[23] <= 1'b 1;	
            end
         else
            begin
            stat[23] <= 1'b 0;	
            end

        //  24: ControlFramesOK)
         if (rsv_stat_d[20] == 1'b 1 & rsv_stat_d[24] == 1'b 1)
            begin
            stat[24] <= 1'b 1;	
            end
         else
            begin
            stat[24] <= 1'b 0;	
            end

        //  25: aFrameTooLong (good+bad)
            stat[25] <= rsv_stat_d[19];	

        //  26: InRangeLenErr (in otherwise good length frame)
            stat[26] <= rsv_stat_d[18] & ~rsv_stat_d[19] & ~rsv_stat_d[29];

        //  27..30 reserved
        
            stat[27] <= rsv_stat_d[27]; // stacked VLAN
            stat[28] <= 1'b 0;
            stat[29] <= 1'b 0;
            stat[30] <= 1'b 0;

        //  31: errorOctets (all that are not in octetsok)
         if (rsv_stat_d[20] == 1'b 0 | dropped_d == 1'b 1)
            begin
            stat[31] <= 1'b 1;	
            end
         else
            begin
            stat[31] <= 1'b 0;	
            end

        // 32..39 PFC class when good pause received

        `ifdef STAT_PFCCNT_ENA

         if (rsv_stat_d[25] == 1'b 1)   // Good Pause
            begin
            stat[32+PFC_PRIORITIES-1:32] <= rsv_stat_pfc_d;	
            end
         else
            begin
            stat[32+PFC_PRIORITIES-1:32] <= {PFC_PRIORITIES{1'b0}};
            end
        
        `endif

         end
      else if( vec_busy_int==1'b 0 )
         begin
         stat <= {CNTNUM{1'b 0}};	//  keep it asserted for one cycle only
         end

      end
   end

//  remap the MAC register address in the correct memory location
//  -------------------------------------------------------------

assign stats_sel_vld = 1'b 1; //  must always run a read cycle when address space is accessed to avoid host interface lockup.

`ifdef STAT_PFCCNT_ENA

always @(mreg_sel)
   begin : process_1p

        //  a set of 32 counters from 0x0100 to 0x01ff (0x40..0x7f)
   if (mreg_sel > 8'h 3F & mreg_sel < 8'h 80)
      begin
      stats_sel = {1'b 0, mreg_sel[5:1]};	//  the Address, in steps of 64-bit is directly the counter base
      end
        // any access from e0..ff gets a mapped to 32..39. 
        // This allows to reuse this block for TX stats as f0..ff is then also mapped to 32..39.
`ifdef MTIP_M128_PFC_16PRI
   // Relocate the counters for the 16-priorities MAC in 192..255
   else if(mreg_sel > 8'h bf)   
      begin
      stats_sel = {2'b 10, mreg_sel[4:1]};	//  the Address, in steps of 64-bit is directly the counter base
      end
`else
   else if(mreg_sel > 8'h df)   
      begin
      stats_sel = {3'b 100, mreg_sel[3:1]};	//  the Address, in steps of 64-bit is directly the counter base
      end
`endif
   else
      begin
      stats_sel = 6'b 011111;	//  unused entry ? , always null
      end

   end

`else

always @(mreg_sel)
   begin : process_1

        //  a set of 32 counters from 0x0100 to 0x01ff (0x40..0x7f)
   if (mreg_sel > 8'h 3F & mreg_sel < 8'h 80)
      begin
      stats_sel = mreg_sel[5:1];	//  the Address, in steps of 64-bit is directly the counter base
        // stats_sel_vld <= 1'b 1;
      end
   else
      begin
      stats_sel = 5'b 11111;	//  unused entry ? , always null
        // stats_sel_vld <= 1'b 0; -- WARNING: must avoid lockup
      end

   end

`endif

// ----------
// ASSERTIONS
// ----------

`ifdef MTIPASSERTIONS

// synopsys translate_off
// synthesis translate_off

always @(posedge reset or negedge clk)
begin : assertions
        if (reset == 1'b 1)
        begin
        end
        else
        begin
                // ensure no new data while previous not yet served
                // However, this case is protected, meaning the worst thing that can happen is that this very short frame statistics
                // could be dropped, but it does not corrupt the previous vector.
                if( stat_val_d==1'b 1 & stat_val_o_int == 1'b 1 )
                begin
                        $display("ASSERT ***[stats_maprxcnts]: ERROR: New statistics vector arrived while previous not served yet. New vector is ignored.");
                        `ifdef MTIPASSERTIONS_STOP_ON_ERRORS
                        $stop(1);
                        `endif
                end

        end
end

// synthesis translate_on
// synopsys translate_on

`endif

endmodule // module stats_maprxcnts
